/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <mutex>
#include "kh_sle_seek.h"
#include "kh_sle_host.h"
#include "sle_seek_interface.h"
#include "sle_hilog.h"

#ifdef __cplusplus
extern "C" {
#endif

namespace OHOS {
namespace NearLink {
namespace SleStandard {
#define SLE_MAX_SEEK_CALLBACK_NUM  3

static SleSeek *g_sleSeek;
static SleSeekCallbacks *g_sleSeekCallback[SLE_MAX_SEEK_CALLBACK_NUM] = {nullptr};
static std::mutex g_seekCbLock;
bool g_seekServiceInited = false;

// Seek callback
class SleSeekCallbackWapper : public SleSeekCallback {
public:
    void OnSeekStateChange(SleSeekState status)
    {
        SLE_LOGD("OnSeekStateChange state(%s)", SleSeekStateStr(status));
        std::lock_guard<std::mutex> lock(g_seekCbLock);
        for (uint8_t i = 0; i < SLE_MAX_SEEK_CALLBACK_NUM; i++) {
            if (g_sleSeekCallback[i] != nullptr && g_sleSeekCallback[i]->OnSleSeekStateChangeCb != nullptr) {
                g_sleSeekCallback[i]->OnSleSeekStateChangeCb(status);
            }
        }
    }

    void OnSeekResult(SleSeekResult &results)
    {
        std::lock_guard<std::mutex> lock(g_seekCbLock);
        for (uint8_t i = 0; i < SLE_MAX_SEEK_CALLBACK_NUM; i++) {
            if (g_sleSeekCallback[i] != nullptr && g_sleSeekCallback[i]->OnSleSeekResultCb != nullptr) {
                g_sleSeekCallback[i]->OnSleSeekResultCb(&results);
            }
        }
    }
};

SleErrorCode CheckSeekOperation(void)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(g_seekServiceInited == true, SLE_ERR_INTERNAL_WRONG, "seek service not inited");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_sleSeek, SLE_ERR_INTERNAL_WRONG, "seek instance null");
    return SLE_NO_ERROR;
}

static std::shared_ptr<SleSeekCallbackWapper> g_SeekCallbackWapper;

SleErrorCode InitSleSeekService(void)
{
    SLE_LOGI("InitSleSeekService");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_seekServiceInited == false, SLE_ERR_INTERNAL_WRONG, "seek service already inited error!");
    if (g_sleSeek == nullptr) {
        g_sleSeek = &SleSeek::GetDefaultSeek();
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(g_sleSeek, SLE_ERR_INTERNAL_WRONG, "seek instance null error!");
    g_SeekCallbackWapper = std::make_shared<SleSeekCallbackWapper>();
    SLE_ASSERT_AND_RETURN_RET_LOG(g_SeekCallbackWapper, SLE_ERR_INTERNAL_WRONG, "callback wapper error!");
    SleErrorCode ret = g_sleSeek->RegisterSeekCallback(g_SeekCallbackWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "init seek service failed!");
    g_seekServiceInited = true;
    return SLE_NO_ERROR;
}

SleErrorCode DeinitSleSeekService(void)
{
    SLE_LOGI("DeinitSleSeekService");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_seekServiceInited == true, SLE_NO_ERROR, "seek service not inited error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_sleSeek, SLE_ERR_INTERNAL_WRONG, "seek instance null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_SeekCallbackWapper, SLE_ERR_FAILED, "callback wapper error!");
    SleErrorCode ret = g_sleSeek->UnregisterSeekCallback(g_SeekCallbackWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "deregist seek callback fail");
    g_seekServiceInited = false;
    return SLE_NO_ERROR;
}

SleErrorCode StartSeek(const SleSeekParam *param)
{
    SLE_LOGI("StartSeek");
    SLE_ASSERT_AND_RETURN_RET_LOG(param, SLE_ERR_INVALID_PARAM, "seek param null");
    SleErrorCode ret = CheckSeekOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleSeek->StartSeek(*param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "start seek failed!");
    return SLE_NO_ERROR;
}

SleErrorCode StopSeek()
{
    SLE_LOGI("StopSeek");
    SleErrorCode ret = CheckSeekOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleSeek->StopSeek();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "stop seek failed!");
    return SLE_NO_ERROR;
}

SleErrorCode GetDefaultSeekParamConfig(SleSeekParam *seekConfig)
{
    SLE_LOGI("GetDefaultSeekParamConfig");
    SLE_ASSERT_AND_RETURN_RET(seekConfig != NULL, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = CheckSeekOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleSeekParam seekParamCfg;
    ret = g_sleSeek->GetDefaultSeekParamConfig(seekParamCfg);
    memcpy(seekConfig, &seekParamCfg, sizeof(SleSeekParam));
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return SLE_NO_ERROR;
}

SleErrorCode RegisterSeekCallbacks(SleSeekCallbacks *seekCallback)
{
    SLE_LOGI("RegisterSeekCallbacks");
    SLE_ASSERT_AND_RETURN_RET_LOG(seekCallback, SLE_ERR_INVALID_PARAM, "seek callback error!");
    SleErrorCode ret = CheckSeekOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::lock_guard<std::mutex> lock(g_seekCbLock);
    for(int i = 0; i < SLE_MAX_SEEK_CALLBACK_NUM; i++) {
        SLE_ASSERT_AND_RETURN_RET_LOG(g_sleSeekCallback[i] != seekCallback, SLE_ERR_INVALID_PARAM, "seek callback has registered!");
    }
    ret = SLE_ERR_FAILED;
    for(int i = 0; i < SLE_MAX_SEEK_CALLBACK_NUM; i++) {
        if (g_sleSeekCallback[i] == nullptr) {
            g_sleSeekCallback[i] = seekCallback;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register seek callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode UnregisterSeekCallbacks(SleSeekCallbacks *seekCallback)
{
    SLE_LOGI("UnregisterSeekCallbacks");
    SLE_ASSERT_AND_RETURN_RET_LOG(seekCallback, SLE_ERR_INVALID_PARAM, "seek callback error!");
    SleErrorCode ret = CheckSeekOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    bool registered = false;
    std::lock_guard<std::mutex> lock(g_seekCbLock);
    for(int i = 0; i < SLE_MAX_SEEK_CALLBACK_NUM; i++) {
        if (g_sleSeekCallback[i] == seekCallback) {
            g_sleSeekCallback[i] = nullptr;
            registered = true;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(registered == true, SLE_ERR_INVALID_PARAM, "seek callback has not registered!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS

#ifdef __cplusplus
}
#endif